package com.eurobcoin.worker;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.mail.Message;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.eurobcoin.ApplicationConst;
import com.eurobcoin.dao.mapper.MailMapper;
import com.eurobcoin.dao.mapper.WorkerStatusMapper;
import com.eurobcoin.dao.model.Mail;
import com.eurobcoin.dao.model.MailExample;
import com.eurobcoin.dao.model.WorkerStatus;
import com.eurobcoin.helper.TextsHelper;
import com.eurobcoin.manager.DAOManager;

public class MailWorker implements Runnable {

	private static final long SLEEP_INTERVAL = 10000;
	
	private static final Logger logger = LoggerFactory.getLogger(MailWorker.class);
	
	private Thread myThread;
	
	private boolean running = false;
	
	@Autowired
	private DAOManager daoManager;
	
	@Autowired
	private Session mailSession;
	
	private List<MailWorkerStatusModel> status;
	
	public MailWorker() {
		this.status = new ArrayList<MailWorkerStatusModel>();
	}
	
	public List<MailWorkerStatusModel> getStatus() {
		return this.status;
	}
	
	public void setStart(boolean start) {
		if(start) {
			this.myThread = new Thread(this, "MAIL_SENDER_THREAD");
			this.running = true;
			this.myThread.start();
		}
	}
	
	public void run() {
		
		while(this.running) {
			
			MailWorkerStatusModel myStatus = new MailWorkerStatusModel();
			
			if(this.daoManager != null && this.mailSession != null) {
				
				SqlSession session = this.daoManager.openSession();
				WorkerStatusMapper mapper = session.getMapper(WorkerStatusMapper.class);
				
				String workerName = TextsHelper.extractClassName(this.getClass().getName());
				WorkerStatus status = mapper.selectByPrimaryKey(workerName);
				
				boolean executing = status.getWor_status().equals(ApplicationConst.YES_VALUE);
				boolean commit = false;
				
				if(!executing) {
					
					myStatus.setError(false);
					myStatus.setRunDate(new Date());
					
					this.switchExecutingFlag(ApplicationConst.YES_VALUE);
					
					// process pending emails
					MailMapper mailMapper = session.getMapper(MailMapper.class);
					MailExample mailExample = new MailExample();
					com.eurobcoin.dao.model.MailExample.Criteria mailCriteria = mailExample.createCriteria();
					mailCriteria.andMai_sendEqualTo(ApplicationConst.NO_VALUE);
					mailCriteria.andMai_errorEqualTo(ApplicationConst.NO_VALUE);
					
					int count = mailMapper.countByExample(mailExample);
					while(count != 0) {
						
						RowBounds rb = new RowBounds(0, 10);
						
						List<Mail> mails = mailMapper.selectByExampleWithRowbounds(mailExample, rb);
						
						for(Mail mail : mails) {
							commit = true;
							
							boolean error = false;
							
							try {
								// send email
								this.sendEmail(
										mail.getMai_to_address(), 
										mail.getMai_to_name(), 
										mail.getMai_subject(), 
										mail.getMai_body());
							}catch(Exception e){
								logger.error("An error occured", e);
								error = true;
								
								myStatus.setError(true);
							}
							
							// update
							if(!error) {
								mail.setMai_send(ApplicationConst.YES_VALUE);
								mail.setMai_send_time(new Date());
								mailMapper.updateByPrimaryKey(mail);
								
								int cnt = myStatus.getEmailsSent();
								cnt++;
								myStatus.setEmailsSent(cnt);
								
							}else{
								mail.setMai_error(ApplicationConst.YES_VALUE);
								mailMapper.updateByPrimaryKey(mail);
							}
						}
						count = mailMapper.countByExample(mailExample);
					}
					
				}
				
				if(commit) {
					session.commit();
				}
				
				session.close();
				
				if(!executing) {
					this.switchExecutingFlag(ApplicationConst.NO_VALUE);
				}
				
			} else {
				logger.warn("Worker not initialized yet, skipping cycle.");
			}
			
			Date d = new Date();
			long nextRun = d.getTime() + SLEEP_INTERVAL;
			d = new Date(nextRun);
			myStatus.setNextRun(d);
			
			this.status.add(0, myStatus);
			
			if(this.status.size() > 10)
				this.status.remove(10);
			
			try {
				Thread.sleep(SLEEP_INTERVAL);
			}catch(InterruptedException e){
				logger.error("An error occured", e);
			}
			
		}
		
	}
	
	private void switchExecutingFlag(String value) {
		SqlSession session = this.daoManager.openSession();
		WorkerStatusMapper mapper = session.getMapper(WorkerStatusMapper.class);
		
		String workerName = this.getClass().getCanonicalName();
		WorkerStatus status = new WorkerStatus();
		status.setWor_id(workerName);
		status.setWor_status(value);
		
		mapper.updateByPrimaryKey(status);
		
		session.commit();
		session.close();
	}
	
	public void stop() {
		this.running = false;
	}
	
	private void sendEmail(
			String toEmail, 
			String toName, 
			String subject, 
			String body) throws MessagingException, UnsupportedEncodingException {

		Message msg = new MimeMessage(this.mailSession);

		msg.setSubject(subject);
		
		msg.setRecipient(RecipientType.TO,
				new InternetAddress(toEmail, toName));
		
		msg.setContent(body, "text/html; charset=UTF-8");
		
		Transport.send(msg);
	}
	
	public void queueEmail(
			String toName, 
			String toAddress, 
			String subject, 
			String body,
			SqlSession sqlSession) {
		
		MailMapper mapper = sqlSession.getMapper(MailMapper.class);
		
		Mail mail = new Mail();
		mail.setMai_to_address(toAddress);
		mail.setMai_to_name(toName);
		mail.setMai_subject(subject);
		mail.setMai_body(body);
		mail.setMai_send(ApplicationConst.NO_VALUE);
		mail.setMai_error(ApplicationConst.NO_VALUE);
		
		mapper.insert(mail);
		
	}
	
}
